use std::sync::Arc;

use ane_system::dao::{ResourceVo, RoleResourceDo, RoleVo};
use axum::{
    extract::{Path, Query},
    response::IntoResponse,
    routing::{get, put},
    Extension, Router,
};
use casbin::MgmtApi;
use common::page::{PageInfo, PageParams};
use sea_orm::TransactionTrait;
use serde::Deserialize;

use crate::{
    error::{AppError, AppResult},
    extract::{Json, ValJson},
    security::Security,
    utils::casbin_policies,
    AppState,
};

use super::SysFilter;

/// [Role](ane_system::entity::sys_role)操作相关的路由
/// - `{prefix}/` `GET|POST` [Role](ane_system::entity::sys_role)的查询和创建
/// - `{prefix}/:id` `PUT|DELETE` [Role](ane_system::entity::sys_role)的更新和删除
/// - `{prefix}/resource/:id` `GET|PUT` [Role](ane_system::entity::sys_role)资源的获取和更新
pub fn get_route<S>(prefix: &str) -> Router<S>
where
    S: Sync + Send + Clone + 'static,
{
    let f = move |p: &str| -> String { format!("{}{p}", prefix) };
    #[cfg_attr(rustfmt, rustfmt_skip)]
    Router::<S>::new()
        .route(&f("/"), get(list).post(create))
        .route(&f("/:id"), put(update).delete(delete))
        .route(&f("/resource/:id"),get(fetch_role_resource).put(update_role_resource),)
}

pub async fn list(
    Extension(state): Extension<AppState>,
    Query(filter): Query<SysFilter>,
) -> AppResult<Json<PageInfo<RoleVo>>> {
    ane_system::dao::Role::list(
        state.db(),
        filter.page.zero_page(),
        filter.page.page_size(),
        filter.name,
        filter.key,
        filter.status,
        0,
    )
    .await
    .map(|(datas, total)| PageInfo::new(datas, &filter.page, total))
    .map(Json)
    .map_err(Into::into)
}

pub async fn create(
    Extension(state): Extension<AppState>,
    ValJson(data): ValJson<ane_system::dao::role::RoleDo>,
) -> AppResult<impl IntoResponse> {
    let txn = state.db().begin().await?;
    if ane_system::dao::Role::get_by_key::<RoleVo>(&txn, &data.key, 0)
        .await?
        .is_some()
    {
        Err(AppError::BadParam(format!("role `{}` exists", data.key)))?;
    }
    let res = ane_system::dao::Role::create(&txn, data)
        .await
        .map(Json)
        .map_err(Into::into);
    txn.commit().await?;
    res
}

/// 更新role信息
///
/// 当status改变时, 需要更新casbin
pub async fn update(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
    ValJson(data): ValJson<ane_system::dao::RoleDo>,
) -> AppResult<impl IntoResponse> {
    let db = state.db();
    let role = ane_system::dao::Role::get_by_id::<RoleVo>(db, id, 0)
        .await?
        .ok_or(AppError::NotFount)?;
    let need_update = role.status != data.status;
    let status = data.status;
    let update_data_scope = role.data_scope != data.data_scope;

    let txn = state.db().begin().await?;
    if matches!(ane_system::dao::Role::get_by_key::<RoleVo>(&txn, &data.key, 0).await?,Some(r) if r.id!=id)
    {
        Err(AppError::BadParam(format!("role `{}` exists", data.key)))?;
    }
    let res = ane_system::dao::Role::update(&txn, id, data)
        .await
        .map(Json)
        .map_err(Into::into);
    if update_data_scope {
        let ids = ane_system::dao::Role::get_user_id(&txn, id, None).await?;
        Security::clean_users_cache(&state, ids).await;
    }
    txn.commit().await?;

    if need_update {
        state.reload_policies().await?;
    }
    res
}

/// 删除role信息，更新casbin
pub async fn delete(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
) -> AppResult<impl IntoResponse> {
    let res = ane_system::dao::Role::delete(state.db(), id)
        .await
        .map_err(Into::into)
        .map(|_| "Ok");
    state.reload_policies().await?;
    res
}

pub async fn fetch_role_resource(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
) -> AppResult<Json<Vec<ane_system::dao::RoleResourceVo>>> {
    ane_system::dao::Role::get_resource(state.db(), id, None)
        .await
        .map(Json)
        .map_err(Into::into)
}

/// 更新一个role的资源，同时更新casbin
pub async fn update_role_resource(
    Extension(state): Extension<AppState>,
    Path(id): Path<i32>,
    Json(res): Json<Vec<RoleResourceDo>>,
) -> AppResult<impl IntoResponse> {
    let res = ane_system::dao::Role::update_resource(state.db(), id, res)
        .await
        .map(Json)
        .map_err(Into::into);
    let role_id = format!("role_{id}");
    state.reload_policies().await?;
    res
}
